# 指定文件编码为UTF-8
# coding: utf-8

"""
overfit_dropout.py 功能说明：
1. 演示神经网络过拟合现象及Dropout正则化的效果
2. 使用MNIST数据集，通过减少训练数据量(300个样本)人为制造过拟合
3. 比较使用Dropout和不使用Dropout时的训练集/测试集准确率差异
4. 可视化训练过程中的准确率变化曲线

关键点：
- 使用6层深度网络(每层100个神经元)增强过拟合倾向
- Dropout通过在训练时随机丢弃部分神经元防止过拟合
- 训练301个epoch观察长期训练效果
- 使用SGD优化器，学习率0.01
- 绘制训练/测试准确率曲线对比图
"""

# 导入操作系统模块
import os
# 导入系统模块
import sys
# 添加父目录到系统路径，以便导入父目录中的模块
sys.path.append(os.pardir)

# 导入NumPy数值计算库
import numpy as np
# 导入matplotlib绘图库
import matplotlib.pyplot as plt
# 从dataset.mnist导入MNIST数据加载函数
from dataset.mnist import load_mnist
# 从common.multi_layer_net_extend导入扩展的多层网络类
from common.multi_layer_net_extend import MultiLayerNetExtend
# 从common.trainer导入训练器类
from common.trainer import Trainer

# 加载MNIST数据集(自动归一化)
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 人为减少训练数据量到300个样本，以制造过拟合
x_train = x_train[:300]
t_train = t_train[:300]

# Dropout参数设置 =================# ... 省略其他代码 ...


# ==== 修改后代码 ====
use_dropout = True  # 是否使用Dropout(True/False)
dropout_ratio = 0.2  # 神经元丢弃概率
# ===============================# ... 省略其他代码 ...


# ==== 修改后代码 ====

# 初始化6层神经网络(每层100个神经元)
network = MultiLayerNetExtend(
    input_size=784,
    hidden_size_list=[100, 100, 100, 100, 100, 100],
    output_size=10,
    use_dropout=use_dropout,
    dropout_ration=dropout_ratio
)

# 创建训练器实例
trainer = Trainer(
    network,
    x_train, t_train, x_test, t_test,
    epochs=301,  # 训练轮数
    mini_batch_size=100,  # 批量大小
    optimizer='sgd',  # 使用SGD优化器
    optimizer_param={'lr': 0.01},  # 学习率0.01
    verbose=True  # 显示训练进度
)

# 开始训练
trainer.train()

# 获取训练过程中的准确率记录
train_acc_list, test_acc_list = trainer.train_acc_list, trainer.test_acc_list

# 结果可视化 ===# ... 省略其他代码 ...


# ==== 修改后代码 ====
# 设置绘图标记样式
markers = {'train': 'o', 'test': 's'}
# 创建x轴数据(epoch数)
x = np.arange(len(train_acc_list))
# 绘制训练集准确率曲线(圆圈标记)
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
# 绘制测试集准确率曲线(方块标记)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
# 设置坐标轴标签
plt.xlabel("epochs")
plt.ylabel("accuracy")
# 设置y轴范围
plt.ylim(0, 1.0)
# 显示图例
plt.legend(loc='lower right')
# 显示图形
plt.show()

"""
预期结果分析：
1. 不使用Dropout时：
   - 训练准确率会快速上升接近100%
   - 测试准确率会明显低于训练准确率(过拟合)
2. 使用Dropout时：
   - 训练准确率上升较慢
   - 测试准确率与训练准确率差距缩小
   - 最终测试性能通常更好

注意事项：
1. Dropout只在训练时启用，测试时关闭
2. Dropout比例通常设置在0.2-0.5之间
3. 网络越深/越宽，Dropout效果通常越明显
4. 可以尝试调整学习率和Dropout比例观察效果变化
"""
